#!/usr/bin/python

import numpy as np
import matplotlib.pyplot as plt
import sys
import os
from StringIO import StringIO
from constants import Constants

class DataCollection(object):
	
	config = {'show_diff': False, 'show_step': False, 'show_step_obs': False, 'show_thr': False}
	
	names = ['time', 'voltage', 'temperature', 'humidity', 'PAR', 'TSR']
	
	scale = {'voltage': 0.1, 'temperature': 1, 'humidity': 1, 'PAR': 1, 'TSR': 1}
	
	lines = { }
	
	def __init__(self, simulate=False):
		self.simulate = simulate
		self.lines = dict()
		self.xmin = 0
		self.xmax = 1000
		self._window = 0
		
	def get_window(self):
		'''Length of the time window.'''
		return self._window
	def set_window(self, value):
		'''Set the length of the time window.
		Setting to 0 maxes it expand to fit time.'''
		self._window = value
	window = property(get_window, set_window)
	
	def load(self, file_name):
		"""Open file and read initial data."""
		
		self.infile = open(file_name, "r")

		# start with at least one entry
		self.data = np.genfromtxt(StringIO('0 0 0 0 0 0'), names=self.names, comments='#')
		self.step_data = np.empty([0,2])
		# load actual data if available
		self.update_data()
		
	def _create_lines(self):
		for i in self.data.dtype.names[1:]:
			#self.lines[i], = plt.plot(self.data['time'], self.data[i], animated=True, label=i)
			# self.data is bootstrapped with zero entries
			self.lines[i], = plt.plot(self.data['time'], self.scale[i]*self.data[i], label=i)
			
		if self.config['show_diff']:
			self.lines['light_diff'],= plt.plot(self.data['time'], self.data['time'], label='light change')
		if self.config['show_thr']:
			self.lines['threshold'], = plt.plot([0,1e8], [Constants.THR_LIGHT,Constants.THR_LIGHT], label='threshold')
		if self.config['show_step']:
			self.lines['step'],      = plt.plot(self.data['time'], self.data['time'], label='time step size')
		if self.config['show_step_obs']:
			self.lines['step_obs'],  = plt.plot(self.data['time'], self.data['time'], label='observed step size')

		plt.ylim(0, 1500)
		plt.ylabel('value')
		plt.xlabel('time [ms]')
		plt.legend(loc=9)

	def plot(self):
		# create lines if it is the first time we plot
		if len(self.lines) < 1:
			self._create_lines()
			
		for i in self.data.dtype.names[1:]:
			self.lines[i].set_data(self.data['time'], self.scale[i]*self.data[i])
			
		if self.config['show_diff']:
			self.lines['light_diff'].set_data(self.data['time'][1:],
				np.max( [ np.diff(self.data['PAR']), np.diff(self.data['TSR']) ], axis=0) )
		if self.config['show_step']:
			self.lines['step'].set_data(self.step_data[:,0], np.multiply(self.step_data[:,1], 1/6000.0) + 400)
		if self.config['show_step_obs']:
			self.lines['step_obs'].set_data(self.data['time'][1:], np.diff(self.data['time'])*5e-4 + 500)
		
		#plt.xlim(self.data['time'][[0,-1]])
		plt.xlim(xmax=self.data['time'][-1])
		if (self.window > 0):
			plt.xlim(xmin=self.data['time'][-1] - self.window)

		plt.draw()
		#plt.draw_if_interactive()

	def update_data(self):
		if self.simulate:
			newdata = [ self.infile.readline() ]
		else:
			newdata = self.infile.readlines()

		if len(newdata) > 0:
			# check for alarms
			for si in [x for x in range(len(newdata)) if (len(newdata[x]) > 0 and newdata[x][0] == '#')]:
				try:
					s = newdata[si].split()
					# s should be "# alarm source broken type time"
					if s[4] == '1':
						# Alarm, bad node
						plt.annotate("A"+s[3], (s[5], 100))
					elif s[4] == '2':
						# Recover
						plt.annotate("R"+s[3], (s[5], 90))
					elif s[4] == '3':
						# Step change
						# step message does not contain time, so we try to get it from the data entry coming before
						while si >= 0 and newdata[si][0] == '#':
							si = si-1
						if si < 0:
							t = self.data['time'][-1]
						else:
							t = newdata[si].split()[0]
						#plt.annotate("Step "+s[5], (t, 120))
						self.step_data = np.append(self.step_data, [[int(t), int(s[5])]], axis=0)
					else:
						print "Unknown alarm type "+s[4]+" in "+' '.join(s)
				except Exception as e:
					print s
					print e
					pass
			
			# update data
			try:
				newdata = np.genfromtxt(StringIO(''.join(newdata)), names=self.names, comments='#')
				self.data = np.append(self.data, newdata)
				return True
			except:
				# the code above fails when newdata contains only comments
				pass
#		print("updated " + str(self.data.size))
		return False
			
	def update(self):
		if self.update_data():
			self.plot()
		
	


def main():
	config = {'ui': True, 'follow': False, 'simulate': False}
	
	if '-f' in sys.argv:
		config['follow'] = True
	if '-sim' in sys.argv:
		config['simulate'] = True
		config['follow'] = True

	data = DataCollection(config['simulate'])
	if '-all' in sys.argv:
		#data.config['show_diff'] = True
		data.config['show_step'] = True
		data.config['show_step_obs'] = True
		#data.config['show_thr'] = True
	
	try:
		data.load(sys.argv[1])
	except:
		print "Usage: "+sys.argv[0]+' file [-f][-sim][-w]'
		sys.exit(1)

	if '-w' in sys.argv:
		data.window = 50000
	data.plot()
	
	if config['follow']:
		timer = plt.figure(1).canvas.new_timer()
		timer.add_callback(data.update)
		timer.start()

	plt.show()
	

if __name__ == '__main__':
	main()
